package tool.code.generater;


import com.alibaba.druid.DbType;
import com.alibaba.druid.util.StringUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import tool.code.generater.config.DDLCoderConfig;
import tool.code.generater.sampler.TableInfoSampler;
import tool.code.generater.struct.ColumnDef;
import tool.code.generater.struct.SamplerType;
import tool.code.generater.struct.TableDef;
import tool.code.generater.template.VelocityTemplate;
import tool.code.generater.util.CommonUtil;
import tool.code.generater.util.StrKit;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        DDLCoderConfig ddlCoderConfig = getDdlCoderConfig(args);
        TableInfoSampler tableInfoSampler = getTableInfoSampler(ddlCoderConfig);
        if (tableInfoSampler == null) return;
        System.setProperty("ddlCoderConfig.template.mode", ddlCoderConfig.getTemplateConfig().getMode());
        doCodeGenerate(tableInfoSampler, ddlCoderConfig);
        long end = System.currentTimeMillis();
        System.out.println("内容生成完成,耗时" + (end - start) + "ms");
    }

    private static void doCodeGenerate(TableInfoSampler tableInfoSampler,
                                       DDLCoderConfig ddlCoderConfig) {
        String templatePath = ddlCoderConfig.getTemplateConfig().getBaseTemplatePath();

        Map<String, String> templateOutputMap = ddlCoderConfig.getTemplateConfig().getTemplateOutputMap();

        File[] files = scanTemplateFiles(templatePath);

        List<TableDef> tableDefs = tableInfoSampler.getColumns();

        VelocityTemplate velocityTemplate = new VelocityTemplate(templatePath);
        String basePackage = ddlCoderConfig.getTemplateConfig().getCustomBasePackage();

        for (TableDef tableDef : tableDefs) {
            String tableNameCamel = StrKit.toCamelCase(tableDef.getName());
            String tableName = StrKit.firstCharToUpperCase(tableNameCamel);
            Map<String, String> configVariableContext = new LinkedHashMap<>();
            configVariableContext.put("tableName", tableName);
            configVariableContext.put("tableNameCamel", tableNameCamel);
            configVariableContext.put("moduleName", ddlCoderConfig.getTemplateConfig().getModuleName());
            ObjectNode customContext = getCustomContextAndAddGlobal(ddlCoderConfig, configVariableContext);

            Map<String, Object> baseContext = new LinkedHashMap<>();
            baseContext.put("custom", customContext);
            baseContext.put("table", tableDef);
            baseContext.put("upperTableNameCamel", tableName);
            baseContext.put("tableNameCamel", tableNameCamel);

            List<ColumnDef> columns = tableDef.getColumns();
            String selectColumns = columns.stream()
                    .map(ColumnDef::getName)
                    .map(n -> String.format("`%s`", n))
                    .collect(Collectors.joining(","));
            baseContext.put("selectColumns", selectColumns);
            baseContext.put("basePackage", basePackage);
            baseContext.put("moduleName", ddlCoderConfig.getTemplateConfig().getModuleName());
            baseContext.put("baseModulePackage", CommonUtil.joinPackage(basePackage, ddlCoderConfig.getTemplateConfig().getModuleName()));
            for (File f : files) {
                configVariableContext.put("templateName", f.getName());
                String target = templateOutputMap.get(f.getName());
                if (StringUtils.isEmpty(target)) {
                    continue;
                }

                target = CommonUtil.dereference(target, configVariableContext);
                File outputFile;
                if (target.startsWith("ind:")) {
                    target = target.substring(4);
                    outputFile = new File(target);
                } else {
                    outputFile = new File(ddlCoderConfig.getTemplateConfig().getBaseOutputPath(), target);
                }

                if ("remove".equalsIgnoreCase(ddlCoderConfig.getTemplateConfig().getMode())) {
                    try {
                        Files.deleteIfExists(outputFile.toPath());
                        System.out.printf("表格：%s 的模板：%s 删除成功，删除目录：%s%n", tableDef.getName(), f.getName(), outputFile.getAbsolutePath());
                        Files.deleteIfExists(Path.of(outputFile.getAbsolutePath() + VelocityTemplate.COPY_SUFFIX));
                        System.out.printf("表格：%s 的模板：%s 删除成功，删除目录：%s%n", tableDef.getName(), f.getName(), outputFile.getAbsolutePath());
                    } catch (IOException e) {
                        System.err.printf("表格：%s 的模板：%s 删除失败，输出目录：%s%n", tableDef.getName(), f.getName(), outputFile.getAbsolutePath());

                    }
                    continue;
                }

                if (!outputFile.getParentFile().exists()) {
                    if (!outputFile.getParentFile().mkdirs()) {
                        System.err.printf("路径: %s 创建失败%n", outputFile.getParentFile().getAbsolutePath());
                        continue;
                    }
                }

                Map<String, Object> context = new LinkedHashMap<>(baseContext);
                try {
                    velocityTemplate.render(f.getName(), context, outputFile);
                } catch (IOException e) {
                    System.err.printf("表格：%s 的模板：%s 生成失败，输出目录：%s%n", tableDef.getName(), f.getName(), outputFile.getAbsolutePath());
                    System.err.printf("错误信息：%s%n", e.getMessage());
                    continue;
                }

                System.out.printf("表格：%s 的模板：%s 生成完成，输出目录：%s%n", tableDef.getName(), f.getName(), outputFile.getAbsolutePath());
            }
            System.out.printf("表格：%s 相关内容生成完成%n", tableDef.getName());
        }
    }

    private static ObjectNode getCustomContextAndAddGlobal(DDLCoderConfig ddlCoderConfig, Map<String, String> configVariableContext) {
        ObjectNode customContext = ddlCoderConfig.getCustomContext();

        //添加到配置变量中
        JsonNode globalVariable = customContext.get("globalVariable");
        if (globalVariable != null && globalVariable.isObject()) {
            Iterator<Map.Entry<String, JsonNode>> iterator = globalVariable.fields();
            while (iterator.hasNext()) {
                Map.Entry<String, JsonNode> entry = iterator.next();
                configVariableContext.putIfAbsent(entry.getKey(), entry.getValue().asText());
            }
        }
        return customContext;
    }

    private static TableInfoSampler getTableInfoSampler(DDLCoderConfig ddlCoderConfig) {
        SamplerType samplerType;
        DbType dbType;
        try {
            samplerType = SamplerType.valueOf(ddlCoderConfig.getSamplerType());
            dbType = DbType.of(ddlCoderConfig.getDbType());
        } catch (IllegalArgumentException e) {
            System.err.println("无效的 samplerType 或 dbType 配置：" + e.getMessage());
            e.printStackTrace();
            return null;
        }

        TableInfoSampler tableInfoSampler = TableInfoSampler.create(samplerType, dbType, ddlCoderConfig.getMappingConfig(), ddlCoderConfig.getJdbcConfig());
        if (tableInfoSampler == null) {
            System.err.println("TableInfoSampler 创建失败");
            return null;
        }
        return tableInfoSampler;
    }

    private static File[] scanTemplateFiles(String templatePath) {
        File file = new File(templatePath);
        File[] files = file.listFiles();
        if (files == null) {
            System.err.println("模板路径无效或无权限访问：" + templatePath);
            System.exit(1);
        }
        return files;
    }

    private static DDLCoderConfig getDdlCoderConfig(String[] args) {
        String config = findOptionValue(args, "--config", "CodeGenerator\\src\\main\\resources\\config.json5");
        DDLCoderConfig ddlCoderConfig;
        try {
            ddlCoderConfig = CommonUtil.fromFileToObject(config, DDLCoderConfig.class);

            Map<String, String> configVariableContext = new LinkedHashMap<>();
            getCustomContextAndAddGlobal(ddlCoderConfig, configVariableContext);
            String jsonStringPretty = CommonUtil.toJsonStringPretty(ddlCoderConfig);
            String dereferenced = CommonUtil.dereference(jsonStringPretty, configVariableContext);
            System.out.println("处理后的配置：");
            System.out.println(dereferenced);
            ddlCoderConfig = CommonUtil.fromJsonToObject(dereferenced, DDLCoderConfig.class);
        } catch (Exception e) {
            System.err.println("配置文件读取失败：" + e.getMessage());
            e.printStackTrace();
            ddlCoderConfig = null;
        }
        if (ddlCoderConfig == null) {
            System.exit(1);
        }
        return ddlCoderConfig;
    }

    public static String findOptionValue(String[] args, String opt, String defaultValue) {
        if (opt == null) return defaultValue;
        for (int i = 0; i < args.length; i++) {
            if (opt.equals(args[i])) {
                return i < args.length - 1 ? args[i + 1] : defaultValue;
            }
        }
        return defaultValue;
    }
}
